package com.jeasonzhao.commons.configuration;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;

import com.jeasonzhao.commons.utils.Algorithms;
import com.jeasonzhao.commons.utils.ResourceException;
import com.jeasonzhao.commons.xml.XMLException;
import com.jeasonzhao.commons.xml.XMLHelper;
import com.jeasonzhao.commons.xml.XMLNode;

public class XMLConfiguration implements IXMLConfiguration
{
    public static final String APPLICATION_CONFIGURATION = "application.configuration.xml";
    public static final String XMLNODE_APPENDERS = "configuration/appender";
    public static final String XMLNODE_PRODUCT = "product";
    private XMLNode m_rootNode = null;
    private java.util.ArrayList<ConfigurationAppender> m_allAppenders = null;
    private java.util.Hashtable<String,IXMLConfiguration> m_cachedConfigurations = null;
    private java.util.HashSet<IXMLConfiguration> m_activatedInstanceList = null;
    private XMLConfiguration()
    {
        try
        {
            onLoadingConfiguration(null,null,null);
        }
        catch(ConfigurationException ex)
        {
            ex.printStackTrace();
        }
    }

    private static volatile XMLConfiguration m_instance = null;
    public static final XMLConfiguration getInstance()
    {
        if(null == m_instance)
        {
            synchronized(XMLConfiguration.class)
            {
                if(null == m_instance)
                {
                    m_instance = new XMLConfiguration();
                }
            }
        }
        return m_instance;
    }

    public void onLoadingConfiguration(XMLNode rootConfigurationNode,String sectionName,XMLNode rootMyNode)
        throws ConfigurationException
    {
        onUnloadingConfiguration(null);
        synchronized(this)
        {
            try
            {
                m_cachedConfigurations = new Hashtable<String,IXMLConfiguration>();
                m_allAppenders = new java.util.ArrayList<ConfigurationAppender>();
                m_activatedInstanceList = new HashSet<IXMLConfiguration>();
                m_rootNode = XMLHelper.fromResource(APPLICATION_CONFIGURATION);
                initOtherConfigurations();
            }
            catch(ResourceException ex)
            {
            }
            catch(XMLException ex)
            {
            }
        }
    }

    private void initOtherConfigurations()
    {
        for(XMLNode node : m_rootNode.selectNodes(XMLNODE_APPENDERS))
        {
            try
            {
                ConfigurationAppender cfg = ConfigurationHelper.getConfigurationAppender(node);
                if(null != cfg)
                {
                    IXMLConfiguration cfgInstance = cfg.getConfigurationInstance();
                    m_cachedConfigurations.put(cfgInstance.getClass().getName(),cfgInstance);
                    m_allAppenders.add(cfg);
                    if(cfg.getMode() == ConfigurationAppenderMode.Normal)
                    {
                        activateConfiguration(cfg,cfgInstance);
                    }
                }
            }
            catch(ConfigurationException ex1)
            {
                ex1.printStackTrace();
            }
        }

    }

    private void activateConfiguration(ConfigurationAppender a,IXMLConfiguration cfgInstance)
        throws ConfigurationException
    {
        if(m_activatedInstanceList.contains(cfgInstance))
        {
            return;
        }
        XMLNode node = null;
        if(Algorithms.isEmpty(a.getFileName()) == false)
        {
            try
            {
                node = XMLHelper.fromResource(cfgInstance.getClass(),a.getFileName());
            }
            catch(ResourceException ex)
            {
                throwException(a.getFileName(),ex.getMessage(),ex);
            }
            catch(XMLException ex)
            {
                throwException(a.getFileName(),ex.getMessage(),ex);
            }
        }
        if(node == null && Algorithms.isEmpty(a.getSectionName()) == false)
        {
            node = this.m_rootNode.selectNode(a.getSectionName());
        }
        cfgInstance.onLoadingConfiguration(this.m_rootNode
                                           ,Algorithms.isEmpty(a.getFileName()) ? a.getSectionName() : a.getFileName()
                                           ,node);
        m_activatedInstanceList.add(cfgInstance);
    }

    public IXMLConfiguration getConfigurationInstance(Class<?> cls)
        throws ConfigurationException
    {
        synchronized(this)
        {
            if(null == m_cachedConfigurations)
            {
                return null;
            }
            else
            {
                IXMLConfiguration ret = m_cachedConfigurations.get(cls.getName());
                if(null == ret)
                {
                    throw new ConfigurationException("Can not found configuration from class " + cls.getName());
                }
                if(null != m_allAppenders)
                {
                    for(ConfigurationAppender a : m_allAppenders)
                    {
                        if(a.getClassName().equals(ret.getClass().getName()))
                        {
                            if(a.getMode().equals(ConfigurationAppenderMode.Normal) == false &&
                               this.m_activatedInstanceList.contains(ret) == false)
                            {
                                this.activateConfiguration(a,ret);
                            }
                        }
                    }
                }
                return ret;
            }
        }
    }

    public void onUnloadingConfiguration(XMLNode rootConfigurationNode)
    {
        synchronized(this)
        {
            if(null == m_activatedInstanceList)
            {
                return;
            }
            for(Iterator<IXMLConfiguration> it = m_activatedInstanceList.iterator();it.hasNext();)
            {
                it.next().onUnloadingConfiguration(this.m_rootNode);
            }
            m_cachedConfigurations = null;
            m_allAppenders = null;
            m_activatedInstanceList = null;
            m_rootNode = null;
        }
    }

    protected void throwException(String str,Throwable ex)
        throws ConfigurationException
    {
        throwException(null,str,ex);
    }

    protected void throwException(String str)
        throws ConfigurationException
    {
        throwException(null,str,null);
    }

    public void throwException(String strFileName,String str)
        throws ConfigurationException
    {
        throwException(strFileName,str,null);
    }

    public void throwException(String strFileName,String str,Throwable ex)
        throws ConfigurationException
    {
        String strMsg = "[" + (Algorithms.isEmpty(strFileName) ? APPLICATION_CONFIGURATION : strFileName) + "]";
        strMsg += Algorithms.isEmpty(str) ? "" : ("," + str);
        if(null != ex)
        {
            strMsg += "," + ex.getMessage();
        }
        throw new ConfigurationException(strMsg,ex);
    }
}
